/* 
 * File:   main.cpp
 * Author: Luciano
 *
 * Created on 5 de Setembro de 2011, 16:48
 */
#include "bib.h"
//bibliotecas padrão
#include <cstdlib>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

//bibliotecas para sockets
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

//bibliotecas para string
#include <string>
#include <string.h>
//#define _GNU_SOURCE

// biblioteca para paralelizacao
#include <pthread.h>

//detalhes servidor
#define SERVER "lam(2011)"


using namespace std;

/*
 * 
 */
char caminho_base[2048];

int main(int argc, char** argv) {

    if (!argc==3){
        cout<< "Numero de argumentos incompletos, favor digitar o diretorio e a porta do bind"<<endl;
    }

    strcpy(caminho_base,argv[1]);
    int porta = atoi(argv[2]);
    int *porta_em_escuta;
    printf("chegou aqui1\n");
    //vamos abrir uma porta para aguardar uma requisicao
    porta_em_escuta=cria_socket(porta);
printf("chegou aqui3\n");
    if(listen(*porta_em_escuta, 5) < 0)
        cout<<"Falha ao tentar escutar o socket do servidor"<<endl;
    //vamos estabelecer uma conexao na porta aberta
    cria_conexao(porta_em_escuta);
    printf("chegou aqui2\n");
    free(porta_em_escuta);
    return 0;
}

int *cria_socket(int porta)
{
    int *porta_em_escuta;
    struct sockaddr_in servidor;

    porta_em_escuta = (int *) malloc(sizeof(int));

    // vamos puxar a funcao socket para definir o tipo de protocol
    // 0- para IP , 6 - TCP e 17 para UDP
    // sock_stream para TCP ou sock_dgram para UDP
  if((*porta_em_escuta = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  {
    cout<<"Falha ao criar o socket"<<endl;
    exit(1);
  }

    // vamos agora colocar os dados no servidor

    // vamos limpar a estrutura servidor
    bzero(&servidor, sizeof(servidor));
    
    //definir o ip em que o servidor ira trabalhar
    servidor.sin_addr.s_addr=htonl(INADDR_ANY); //para aceitar qualquer ip

    //vamos definir a familia
    servidor.sin_family=AF_INET;

    // vamos definir a porta
    servidor.sin_port=htons(porta);//converte int para byte de rede tipo tcp/ip

    // vamos fazer o bind
    if(bind(*porta_em_escuta, (struct sockaddr *) &servidor, sizeof(servidor)) < 0)
    {
        cout<<"Nao conseguiu fazer o bind"<<endl;
        exit(1);
    }
    
    return porta_em_escuta;
}


static void *executa_thread(void *p_tr)
{
    int socket_cliente;
    socket_cliente= *((int *)p_tr);
    pthread_detach(pthread_self());//funcao para liberar quando a thread acaba

    executa(socket_cliente); //executa o pedido
    close(socket_cliente); //fecha a conexao
    return NULL;
}


void cria_conexao(int *porta_em_escuta)
{
    //vamos criar agora um socket para o cliente
    struct sockaddr_in cliente;
    socklen_t tam_cliente;

    int socket_cliente;

    //para paralelizacao
    int *p_tr; //ponteiro para trhread
    pthread_t t_id; //identificador da thread

    // para aceitar a conexao
    while(1)
    {
        p_tr = (int *) malloc(sizeof(int));
        *p_tr=accept(*porta_em_escuta, (struct sockaddr *) &cliente, &tam_cliente);
        printf("CHEGOU AQUI, criou thread\n");
        pthread_create(&t_id, NULL, &executa_thread, p_tr);
        //vamos agora fazer a execucao do pedido

    }
    
}

void executa(int socket_cliente)
{
    char buffer[1024], metodo[15], uri[1024], http_version[1024];
    int bytes_recebidos;
    char caminho[2048];
    char buf[1024];
    int flag=0;
    //flag 0-> HTTP/1.1 200 OK
    //flag 1-> HTTP/1.1 400 Bad Request
    //flag 2-> HTTP/1.1 404 Not Found
    //flag 3-> HTTP/1.1 505 HTTP Version Not Supported
    //flag 4-> HTTP/1.1 301 Moved Permanently
    
    bzero(&caminho, 2048*sizeof(char));
    //vamos usar a funcao recv para ler o protocolo do cliente
    // e armazenar em um buffer a string recebida
    if((bytes_recebidos = recv(socket_cliente, buffer, 1024, 0)) < 0)
    {
        sprintf(buf, "Nao foi possivel ler o cabecalho do cliente ");
        send(socket_cliente, buf, strlen(buf), 0);
        exit(1);
    }
    if(bytes_recebidos == 0)
        cout<<"conexao finalizada com sucesso"<<endl;

    printf("BUFFER: %s\n", buffer);
     
    //vamos separar os tokens
    //SEPARA METODO
    int i=0, j=0;
    while(buffer[i]!=32)
    {
        metodo[j]=buffer[i];
        i++;
        j++;
    }
    metodo[j]='\0';
    j=0;
    i++;

    //SEPARA URI
    if(caminho_base[0]=='/' && caminho_base[1]=='\0')
            i++;
    while(buffer[i]!=32)
    {
        //verifica se a URI solicitada nao estah explorando diretorios nao autorizados
        if ((buffer[i]== '.' && buffer[i+1]== '.')||(buffer[i]==92))
        {
            sprintf(buf, "HTTP/1.1 400 Bad Request\r\n ");
            send(socket_cliente, buf, strlen(buf), 0);
            sprintf(buf, "\r\n");
            send(socket_cliente, buf, strlen(buf), 0);
            flag=1;
        }
        else
        {
            uri[j]=buffer[i];
            i++;
            j++;
        }
    }
    uri[j]='\0';
    j=0;
    i++;

    //SEPARA HTTP VERSION
    while(buffer[i]!=32)
    {
        http_version[j]=buffer[i];
        i++;
        j++;
    }
    http_version[j]='\0';

    //vamos verificar a presenca do arquivo
    FILE *fp;
    
    long size;
    char *c;
    

    //caso o caminho base seja o diretorio raiz
    if(caminho_base[0]=='/' && caminho_base[1]=='\0' && flag==0)
    {
	fp= fopen(uri, "rb");
        if (fp == NULL)
        {
            sprintf(buf, "HTTP/1.1 404 Not Found ");
            send(socket_cliente, buf, strlen(buf), 0);
            sprintf(buf, "\n\n");
            send(socket_cliente, buf, strlen(buf), 0);
            flag = 1;
        }
    }
    else
    {
        strcat(caminho,caminho_base);
	strcat(caminho,uri);
	fp= fopen(caminho_base, "rb");
        //server nao possui arquivo
        if (fp == NULL)
        {
            sprintf(buf, "HTTP/1.1 404 Not Found ");
            send(socket_cliente, buf, strlen(buf), 0);
            sprintf(buf, "\n\n");
            send(socket_cliente, buf, strlen(buf), 0);
            flag = 1;
        }

    }    


    //se o pedido for GET
    if(strcmp(metodo,"GET")==0 && flag ==0)
    {
        strcpy(buf, "HTTP/1.1 200 OK\r\n");
        send(socket_cliente, buf, strlen(buf), 0);
        sprintf(buf, "Content-Type: text/html\r\n");
        send(socket_cliente, buf, strlen(buf), 0);
        strcpy(buf, "\r\n");
        send(socket_cliente, buf, strlen(buf), 0);
     //   fgets(buf, sizeof(buf), fp);
        if (fp)
        {
            fseek(fp , 0 , SEEK_END);
            size = ftell(fp);
            rewind(fp);
            c = (char*) malloc(sizeof(char) * size);
            fread(c, 1, size, fp);
            send(socket_cliente, c, size, 0);
            fclose(fp);
            free(c);
        }
        sprintf(buf, "\n\n");
        send(socket_cliente, buf, strlen(buf), 0);
        
    }

    //se o pedido for HEAD
    else if(strcmp(metodo,"HEAD")==0 && flag ==0)
    {
        strcpy(buf, "HTTP/1.1 200 OK\r\n");
        send(socket_cliente, buf, strlen(buf), 0);
        sprintf(buf, "Content-Type: text/html\r\n");
        send(socket_cliente, buf, strlen(buf), 0);
        strcpy(buf, "\r\n");
        send(socket_cliente, buf, strlen(buf), 0);
        sprintf(buf, "\n\n");
        send(socket_cliente, buf, strlen(buf), 0);
    }
    //vamos tratar metodos nao implementados
    else if( flag ==0)
    {
     
     sprintf(buf, "HTTP/1.1 501 Metodo nao implementado\r\n");
     send(socket_cliente, buf, strlen(buf), 0);
     sprintf(buf, "\n\n");
     send(socket_cliente, buf, strlen(buf), 0);
    }
    fclose(fp);
    //fim da comunicacao
}


